package com.fyl.verify.aop;


import com.alibaba.fastjson.JSONObject;
import com.fyl.core.advance.constant.IResponseEnum;
import com.fyl.core.advance.constant.enums.CommonResponseEnum;
import com.fyl.verify.anno.Verify;
import com.fyl.verify.service.VerifyService;
import com.fyl.verify.spring.boot.autoconfigure.VerifyProperties;
import com.fyl.verify.utils.ParamVerifyUtils;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.AnnotationUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;


public class VerifyAnnotationMethodInterceptor implements MethodInterceptor {

    @Lazy
    @Autowired
    VerifyService verifyService;

    @Lazy
    @Autowired
    VerifyProperties verifyProperties;

    @Autowired
    HttpServletRequest request;

    @Autowired
    HttpServletResponse response;


    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        verifyService.preVerifyHandler(request);

        Object[] args = methodInvocation.getArguments();
        Annotation anno = getAnnotation(methodInvocation, Verify.class);

        Verify verify = AnnotationUtils.getAnnotation(anno, Verify.class);
        Object auth = AnnotationUtils.getValue(anno, Verify.AUTH);
        Object param = AnnotationUtils.getValue(anno, Verify.PARAM);
        boolean isAuthVerify = Objects.nonNull(auth) ? (boolean) auth : verify.auth();
        boolean isParamVerify = Objects.nonNull(param) ? (boolean) param : verify.param();

        if (isAuthVerify) {
            IResponseEnum responseEnum = verifyService.authVerifyHandler(request, response);
            if (!Objects.equals(CommonResponseEnum.SUCCESS, responseEnum)) {
                verifyService.verifyErrorHandler(responseEnum, response);
                return null;
            }
        }

        if (isParamVerify && args.length > 0) {
            if (verifyProperties.isDeepMode()) {
                // 深校验
                List<String> messages = new ArrayList<>();
                for (int i = 0; i < args.length; i++) {
                    Object arg = args[i];
                    messages.addAll(ParamVerifyUtils.deepVerify(arg));
                }
                if (!messages.isEmpty()) {
                    verifyService.verifyErrorHandler(new IResponseEnum() {
                        @Override
                        public int getCode() {
                            return 400;
                        }

                        @Override
                        public String getMessage() {
                            return JSONObject.toJSONString(messages);
                        }
                    }, response);
                    return null;
                }
            } else {
                // 浅校验
                for (int i = 0; i < args.length; i++) {
                    Object arg = args[i];
                    String message = ParamVerifyUtils.verify(arg);
                    if (StringUtils.isNotBlank(message)) {
                        verifyService.verifyErrorHandler(new IResponseEnum() {
                            @Override
                            public int getCode() {
                                return 400;
                            }

                            @Override
                            public String getMessage() {
                                return message;
                            }
                        }, response);
                        return null;
                    }
                }
            }
        }
        Object result = methodInvocation.proceed();
        verifyService.postVerifyHandler(response);
        return result;
    }

    private Annotation getAnnotation(MethodInvocation methodInvocation, Class<? extends Annotation> annotationClass) {
        Method method = methodInvocation.getMethod();
        Annotation[] annotations = AnnotationUtils.getAnnotations(method);
        for (Annotation anno : annotations) {
            if (anno.annotationType().equals(annotationClass) || anno.annotationType().isAnnotationPresent(annotationClass)) {
                return anno;
            }
        }
        Class<?> clazz = methodInvocation.getThis().getClass();
        return AnnotationUtils.getAnnotation(clazz, annotationClass);
    }
}
